package limiter

import (
	"github.com/gogf/gf/net/ghttp"
	"strconv"
	"com.cyt.common/library/response"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/text/gstr"
	limiter "github.com/ulule/limiter/v3"
	libredis "github.com/go-redis/redis/v8"
	ulimiter "github.com/ulule/limiter/v3"
	sredis "github.com/ulule/limiter/v3/drivers/store/redis"
)

// Middleware is the middleware for basic http.Handler.
type Middleware struct {
	Limiter        *limiter.Limiter
	OnError        ErrorHandler
	OnLimitReached LimitReachedHandler
	ExcludedKey    func(string) bool
}

func New(r *ghttp.Request)  {
	// 一分钟60次请求
	rate, err := ulimiter.NewRateFromFormatted("1000-M")
	if err != nil {
		panic(err)
		return
	}

	config := g.Cfg().GetString("redis.default")
	rconfig := gstr.Split(config, ",")
	// Create a redis client.
	option, err := libredis.ParseURL("redis://:" + rconfig[2] + "@" + rconfig[0] + "/" + rconfig[1])
	if err != nil {
		panic(err)
		return
	}
	client := libredis.NewClient(option)

	// Create a store with the redis client.
	store, err := sredis.NewStoreWithOptions(client, limiter.StoreOptions{
		Prefix:   "limiter_gf_api",
		MaxRetry: 3,
	})
	if err != nil {
		panic(err)
		return
	}
	NewMiddleware(ulimiter.New(store, rate)).Handle(r)
}

// NewMiddleware return a new instance of a basic HTTP middleware.
func NewMiddleware(limiter *limiter.Limiter, options ...Option) *Middleware {
	middleware := &Middleware{
		Limiter:        limiter,
		OnError:        DefaultErrorHandler,
		OnLimitReached: DefaultLimitReachedHandler,
		ExcludedKey:    nil,
	}

	for _, option := range options {
		option.apply(middleware)
	}

	return middleware
}

// Handle request.
func (middleware *Middleware) Handle(r *ghttp.Request) {
	key :=  r.GetClientIp()
	if middleware.ExcludedKey != nil && middleware.ExcludedKey(key) {
		r.Middleware.Next()
		return
	}

	context, err := middleware.Limiter.Get(r.Context(), key)
	if err != nil {
		panic(err)
		return
	}

	r.Response.Header().Set("X-RateLimit-Limit", strconv.FormatInt(context.Limit, 10))
	r.Response.Header().Set("X-RateLimit-Remaining", strconv.FormatInt(context.Remaining, 10))
	r.Response.Header().Set("X-RateLimit-Reset", strconv.FormatInt(context.Reset, 10))

	if context.Reached {
		middleware.OnLimitReached(r)
		response.Result(r, response.StatusTooManyRequests, "请求限制")
		return
	}

	r.Middleware.Next()
}